<?php
require_once 'FL/Core.php';

class FL_CMS extends FL_Core {
	static private $_LIST_INSTANCES = array(); 
	/*
	 * tables names
	 */
	const T_TAG_TYPES   = 'tag_types';
	const T_TAGS        = 'tags';
	const T_L_LIST_TAGS = 'list_tags_links';
	const T_REGISTER    = 'register';
	const T_CLUSTERS	= 'clusters';
	const T_TYPES		= 'types';
	const T_L_LISTS		= 'list_links';
	const T_LIST		= 'list';	
	
	/*
	 * installation
	 */
	
	public function install(){
		$s_schema = '';
		
		$s_sql_tag_types = '
		CREATE TABLE IF NOT EXISTS #' . self::T_TAG_TYPES . ' (
  			tag_type_id INT NOT NULL AUTO_INCREMENT,
  			name VARCHAR(45) NULL ,
  		PRIMARY KEY (tag_type_id) )';
  		
  		$s_sql_tags = '
  		CREATE  TABLE IF NOT EXISTS #' . self::T_TAGS . ' (
  			tag_id INT NOT NULL AUTO_INCREMENT ,
  			tag_name VARCHAR(45) NOT NULL ,
  			technical TINYINT(1)  NULL DEFAULT false,
  			tag_type_id INT NULL,
  		PRIMARY KEY (tag_id) ,
  		UNIQUE INDEX tag_name_UNIQUE (tag_name ASC) ,
		INDEX fk_tags_tag_types1 (tag_type_id ASC) ,
		CONSTRAINT fk_tags_tag_types1
		FOREIGN KEY (tag_type_id )
		REFERENCES #' . self::T_TAG_TYPES . ' (tag_type_id )
			ON DELETE SET NULL
			ON UPDATE SET NULL)';
  		
  		$s_sql_list_tags_links = '
  		CREATE  TABLE IF NOT EXISTS #' . self::T_L_LIST_TAGS . ' (
			link_id INT NOT NULL AUTO_INCREMENT,
			tags_tag_id INT NOT NULL ,
			list_item_id INT NULL ,
			register_list_id INT NULL ,
		PRIMARY KEY (link_id) ,
		INDEX fk_list_tags_links_tags1 (tags_tag_id ASC) ,
		INDEX fk_list_tags_links_register1 (register_list_id ASC) ,
		CONSTRAINT fk_list_tags_links_tags1
		FOREIGN KEY (tags_tag_id )
		REFERENCES #' . self::T_TAGS . ' (tag_id )
			ON DELETE CASCADE
			ON UPDATE CASCADE,		
		CONSTRAINT fk_list_tags_links_register1
		FOREIGN KEY (register_list_id )
		REFERENCES #' . self::T_REGISTER . ' (list_id )
			ON DELETE CASCADE
			ON UPDATE CASCADE)';
  		
  		$s_sql_register = '
  		CREATE  TABLE IF NOT EXISTS #' . self::T_REGISTER . ' (
			list_id INT NOT NULL AUTO_INCREMENT,
			types_type_id INT NOT NULL ,
			clusters_cluster_id INT NOT NULL ,
			list_name VARCHAR(255) NULL ,
			list_description TEXT NULL ,
		PRIMARY KEY (list_id) ,
		INDEX fk_register_types (types_type_id ASC) ,
		INDEX fk_register_clusters1 (clusters_cluster_id ASC) ,
		CONSTRAINT fk_register_types
		FOREIGN KEY (types_type_id )
		REFERENCES #' . self::T_TYPES . ' (type_id )
			ON DELETE RESTRICT
			ON UPDATE RESTRICT,
		CONSTRAINT fk_register_clusters1
		FOREIGN KEY (clusters_cluster_id )
		REFERENCES #' . self::T_CLUSTERS . ' (cluster_id )
			ON DELETE RESTRICT
			ON UPDATE RESTRICT)';
  		
  		$s_sql_types = '
  		CREATE  TABLE IF NOT EXISTS #' . self::T_TYPES . ' (
			type_id INT NOT NULL AUTO_INCREMENT ,
			name VARCHAR(45) NOT NULL ,
			handler VARCHAR(128) NOT NULL,
			shema VARCHAR(255) NULL ,
		PRIMARY KEY (type_id) ,
		UNIQUE INDEX name_UNIQUE (name ASC) )';
  		
  		$s_sql_clusters = '
  		CREATE  TABLE IF NOT EXISTS #' . self::T_CLUSTERS . ' (
			cluster_id INT NOT NULL AUTO_INCREMENT ,
			name VARCHAR(45) NULL ,
		PRIMARY KEY (cluster_id) ,
		UNIQUE INDEX name_UNIQUE (name ASC) )';
  		
  		$s_sql_list_links = '
  		CREATE  TABLE IF NOT EXISTS #' . self::T_L_LISTS . ' (
			link_id INT NOT NULL AUTO_INCREMENT ,
			link_name VARCHAR(45) NULL ,
			register_list_id_f INT NOT NULL ,
			register_list_id_t INT NOT NULL ,
			link_type TINYINT NOT NULL DEFAULT 0 ,
		PRIMARY KEY (link_id) ,
		INDEX fk_list_links_register1 (register_list_id_f ASC) ,
		INDEX fk_list_links_register2 (register_list_id_t ASC) ,
		CONSTRAINT fk_list_links_register1
		FOREIGN KEY (register_list_id_f )
		REFERENCES #' . self::T_REGISTER . ' (list_id)
			ON DELETE CASCADE
			ON UPDATE CASCADE,
		CONSTRAINT fk_list_links_register2
		FOREIGN KEY (register_list_id_t )
		REFERENCES #' . self::T_REGISTER . ' (list_id )
			ON DELETE CASCADE
			ON UPDATE CASCADE)';
  		
		self::$_o_dba->query($s_sql_types);
		self::$_o_dba->query($s_sql_clusters);
		self::$_o_dba->query($s_sql_register);
		self::$_o_dba->query($s_sql_tag_types);
		self::$_o_dba->query($s_sql_tags);
		self::$_o_dba->query($s_sql_list_tags_links);
		self::$_o_dba->query($s_sql_list_links);
	}
	
	/*
	 * types managment
	 */
	public function regType($s_name, $s_handler, $s_schema){
		self::$_o_dba->queryInsert(self::T_TYPES, array(
			'name'    => $s_name, 
			'handler' => $s_handler, 
			'scheme'  => $s_schema));
		return self::$_o_dba->nextId();
	}
	
	public function unregType($i_type_id){
		self::$_o_dba->queryDelete(self::T_TYPES, 'type_id', $i_type_id);
	}
	
	public function getTypes(){
		$s_sql = 'SELECT * FROM #' . self::T_TYPES;
		$o_result = self::$_o_dba->query($s_sql);
		return $o_result->fetchAllAsArray();
	}
	
	public function getTypeProps($i_type_id){
		$s_sql = 'SELECT * FROM #' . self::T_TYPES
			. ' WHERE type_id = ' . $i_type_id;
		$o_result = self::$_o_dba->query($s_sql);
		return $o_result->fetchNextAsArray();
	}
	
	/*
	 * clusters management
	 */
	public function regCluster($s_name){
		self::$_o_dba->queryInsert(self::T_CLUSTERS, array('name' => $s_name));
		return self::$_o_dba->nextId();		
	}
	
	public function unregCluster($i_cluster_id){
		self::$_o_dba->queryDelete(self::T_CLUSTERS, 'cluster_id', $i_cluster_id);
	}
	
	public function getClusters(){
		$s_sql = 'SELECT * FROM #' . self::T_CLUSTERS;
		$o_result = self::$_o_dba->query($s_sql);
		return $o_result->fetchAllAsArray();
	}
	
	/*
	 * list management
	 */
	public function regList($s_name, $i_type_id, $i_cluster_id){
		self::$_o_dba->queryInsert(self::T_REGISTER, array(
			'types_type_id'		  => $i_type_id,
			'clusters_cluster_id' => $i_cluster_id,
			'list_name' 		  => $s_name));
		$i_list_id = self::$_o_dba->nextId();
		try {
			$this->_installList($i_list_id, $i_type_id, $i_cluster_id);
		}
		catch (Exception $e) {
			$this->unregList($i_list_id);
			throw $e;
		}
		return $i_list_id;
	}
	
	public function unregList($i_list_id){
		self::$_o_dba->queryDelete(self::T_REGISTER, 'list_id', $i_list_id);
	}
		
	public function setListProps($i_list_id, $a_props){
		self::$_o_dba->queryUpdate(self::T_REGISTER, 'list_id', $i_list_id, $a_props);
	}
	
	/**
	 * @return FL_List_Abstract
	 */
	public function getListInstance($i_list_id){
		if(!empty(self::$_LIST_INSTANCES[$i_list_id])){
			$o_list_instance = self::$_LIST_INSTANCES[$i_list_id];
		}
		else {			
			$a_list_props = $this->getListProps($i_list_id);			
			$this->_loadHandler($a_list_props['handler']);
			$o_list_instance = new $a_list_props['handler'](
				$i_list_id, 
				$a_list_props['types_type_id'], 
				$a_list_props['clusters_cluster_id']);
			self::$_LIST_INSTANCES[$i_list_id] = $o_list_instance;
		}
		return $o_list_instance;
	}
	
	//TODO: implement exception NO DATA FOUND
	public function getListProps($i_list_id){
		$s_sql = 'SELECT r.*, t.handler, t.scheme 
			FROM #' . self::T_REGISTER . ' AS r
			JOIN #' . self::T_TYPES . ' AS t ON t.type_id = r.types_type_id
			WHERE list_id=' . $i_list_id;
		$o_result = self::$_o_dba->query($s_sql);
		return $o_result->fetchNextAsArray();		
	}
	
	public function assignTagToList($i_list_id, $i_tag_id){
		$a_values = array(
			'register_list_id' => $i_list_id,
			'tags_tag_id' => $i_tag_id
		);
		self::$_o_dba->queryInsert(self::T_L_LIST_TAGS, $a_values);
		return self::$_o_dba->nextId();
	}
	
	public function unassignTagFromList($i_list_id, $i_tag_id){
		$s_sql = 'DELETE FROM #' . self::T_L_LIST_TAGS 
				. ' WHERE register_list_id = ' . $i_list_id
				. '   AND tags_tag_id = ' . $i_tag_id
				. '   AND list_item_id IS NULL';
		$o_result = self::$_o_dba->query($s_sql);
	}	
	
	/*
	 * tags management
	 */
	public function regTagType($s_name){
		self::$_o_dba->queryInsert(self::T_TAG_TYPES, array('name' => $s_name));
		return self::$_o_dba->nextId();				
	}
	
	public function unregTagType($i_type_id){
		self::$_o_dba->queryDelete(self::T_TAG_TYPES, 'tag_type_id', $i_type_id);
	}
	
	public function regTag($s_name, $i_type_id, $b_technical = false){
		self::$_o_dba->queryInsert(self::T_TAGS, array(
			'tag_name' 	  => $s_name,
			'tag_type_id' => $i_type_id,
			'technical'   => $b_technical));
		return self::$_o_dba->nextId();		
	}
	
	public function unregTag($i_tag_id){
		self::$_o_dba->queryDelete(self::T_TAGS, 'tag_id', $i_tag_id);
	}
	
	public function setTagProps($i_tag_id, $a_tag_props){
		self::$_o_dba->queryUpdate(self::T_TAGS, 'tag_id', $i_tag_id, $a_tag_props);
	}	
	
	/*
	 * CMS utilities
	 */
	protected function _getListTableName($i_list_id){
		$a_list_props = $this->getListProps($i_list_id);
		return $this->__getListTableName($i_list_id, 
			$a_list_props['types_type_id'],
			$a_list_props['clusters_cluster_id']);
	}
	
	protected function __getListTableName($i_list_id, $i_type_id, $i_cluster_id){
		return self::T_LIST . $i_list_id . $i_type_id . $i_cluster_id;
	}
	
	protected function _getLimits($i_page){
		$i_page_length = self::$a_config[self::CONFVAR_CMS_PAGE_LEN];
		$b_zero_page   = self::$a_config[self::CONFVAR_CMS_ZERO_PAGE];
		$i_page   = $b_zero_page && $i_page > 0 ? $i_page - 1 : $i_page;
		$i_offset = $i_page * $i_page_length;
		return array('limit' => $i_page_length, 'offset' => $i_offset);
	}
	
	private function _installList($i_list_id, $i_type_id, $i_cluster_id){
		$s_table_name = $this->__getListTableName($i_list_id, $i_type_id, $i_cluster_id);
		$a_type_props = $this->getTypeProps($i_type_id);
		$this->_loadHandler($a_type_props['handler']);
		$o_list_instance = new $a_type_props['handler']($i_list_id, $i_type_id, $i_cluster_id);
		self::$_LIST_INSTANCES[$i_list_id] = $o_list_instance;
		$o_list_instance->install();
		return $o_list_instance;
	}

	private function _loadHandler($s_class_name){
		$s_class_path = str_replace('_', '/', $s_class_name) . '.php';
		$b_file_not_found = true;
		$a_inc_paths = explode(PATH_SEPARATOR, get_include_path());
		foreach($a_inc_paths as $inc_path) {
			if(file_exists($inc_path . '/' . $s_class_path)){
				$b_file_not_found = false;
				break;
			}
		}
		if($b_file_not_found)
			throw self::getException(self::ERR_INVALID_INPUT, 'file ' . $s_class_path . ' not found!');
			
		include_once $s_class_path;
		
		if(!class_exists($s_class_name))
			throw self::getException(self::ERR_INVALID_INPUT, 'class ' . $s_class_name . ' not found!');
			
		return true;
	}
}